"""
AI Agent Pro - 工具系统基础类
"""

import asyncio
import json
import time
import uuid
from abc import ABC, abstractmethod
from enum import Enum
from typing import Any, Dict, List, Optional, Union

from pydantic import BaseModel, Field, validator

from app.core.exceptions import ToolError
from app.core.logging import get_logger, log_tool_usage

logger = get_logger(__name__)


class ToolType(str, Enum):
    """工具类型枚举"""
    SEARCH = "search"           # 搜索工具
    COMPUTE = "compute"         # 计算工具
    FILE = "file"              # 文件操作工具
    NETWORK = "network"        # 网络工具
    CODE = "code"              # 代码执行工具
    DATA = "data"              # 数据处理工具
    AI = "ai"                  # AI服务工具
    SYSTEM = "system"          # 系统工具
    CUSTOM = "custom"          # 自定义工具


class ToolStatus(str, Enum):
    """工具状态枚举"""
    IDLE = "idle"
    RUNNING = "running"
    SUCCESS = "success"
    ERROR = "error"
    TIMEOUT = "timeout"


class ToolResult(BaseModel):
    """工具执行结果"""
    success: bool = Field(..., description="是否执行成功")
    data: Any = Field(default=None, description="执行结果数据")
    error: Optional[str] = Field(default=None, description="错误信息")
    metadata: Dict[str, Any] = Field(default_factory=dict, description="元数据")
    execution_time: float = Field(default=0.0, description="执行时间(秒)")
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典格式"""
        return {
            "success": self.success,
            "data": self.data,
            "error": self.error,
            "metadata": self.metadata,
            "execution_time": self.execution_time
        }


class ToolParameter(BaseModel):
    """工具参数定义"""
    name: str = Field(..., description="参数名称")
    type: str = Field(..., description="参数类型")
    description: str = Field(..., description="参数描述")
    required: bool = Field(default=True, description="是否必需")
    default: Any = Field(default=None, description="默认值")
    enum: Optional[List[str]] = Field(default=None, description="枚举值")
    pattern: Optional[str] = Field(default=None, description="正则表达式模式")
    min_value: Optional[Union[int, float]] = Field(default=None, description="最小值")
    max_value: Optional[Union[int, float]] = Field(default=None, description="最大值")


class ToolSchema(BaseModel):
    """工具模式定义"""
    name: str = Field(..., description="工具名称")
    description: str = Field(..., description="工具描述")
    tool_type: ToolType = Field(..., description="工具类型")
    version: str = Field(default="1.0.0", description="工具版本")
    parameters: List[ToolParameter] = Field(default_factory=list, description="参数列表")
    examples: List[Dict[str, Any]] = Field(default_factory=list, description="使用示例")
    tags: List[str] = Field(default_factory=list, description="标签")
    enabled: bool = Field(default=True, description="是否启用")
    timeout: int = Field(default=30, description="超时时间(秒)")
    
    def to_openai_format(self) -> Dict[str, Any]:
        """转换为OpenAI函数调用格式"""
        properties = {}
        required_params = []
        
        for param in self.parameters:
            prop = {
                "type": param.type,
                "description": param.description
            }
            
            if param.enum:
                prop["enum"] = param.enum
            if param.pattern:
                prop["pattern"] = param.pattern
            if param.min_value is not None:
                prop["minimum"] = param.min_value
            if param.max_value is not None:
                prop["maximum"] = param.max_value
            if param.default is not None:
                prop["default"] = param.default
            
            properties[param.name] = prop
            
            if param.required:
                required_params.append(param.name)
        
        return {
            "name": self.name,
            "description": self.description,
            "parameters": {
                "type": "object",
                "properties": properties,
                "required": required_params
            }
        }


class BaseTool(ABC):
    """工具基础抽象类"""
    
    def __init__(self, config: Optional[Dict[str, Any]] = None):
        self.config = config or {}
        self.tool_id = str(uuid.uuid4())
        self.status = ToolStatus.IDLE
        self.execution_count = 0
        self.total_execution_time = 0.0
        self.last_execution_time = 0.0
        self.created_at = time.time()
        
        # 从子类获取工具模式
        self.schema = self.get_schema()
        
        logger.info(f"工具初始化: {self.schema.name} ({self.tool_id})")
    
    @abstractmethod
    def get_schema(self) -> ToolSchema:
        """
        获取工具模式定义
        
        Returns:
            工具模式
        """
        pass
    
    @abstractmethod
    async def _execute(self, **kwargs) -> ToolResult:
        """
        执行工具逻辑 (子类实现)
        
        Args:
            **kwargs: 工具参数
            
        Returns:
            执行结果
        """
        pass
    
    async def execute(self, **kwargs) -> ToolResult:
        """
        执行工具 (公共接口)
        
        Args:
            **kwargs: 工具参数
            
        Returns:
            执行结果
        """
        start_time = time.time()
        execution_id = str(uuid.uuid4())
        
        try:
            # 更新状态
            self.status = ToolStatus.RUNNING
            
            # 验证参数
            self._validate_parameters(kwargs)
            
            # 检查工具是否启用
            if not self.schema.enabled:
                raise ToolError(self.schema.name, "工具已禁用")
            
            logger.info(f"开始执行工具: {self.schema.name}, 执行ID: {execution_id}")
            
            # 设置超时
            try:
                result = await asyncio.wait_for(
                    self._execute(**kwargs),
                    timeout=self.schema.timeout
                )
            except asyncio.TimeoutError:
                self.status = ToolStatus.TIMEOUT
                raise ToolError(self.schema.name, f"工具执行超时 ({self.schema.timeout}秒)")
            
            # 更新统计
            execution_time = time.time() - start_time
            self.execution_count += 1
            self.total_execution_time += execution_time
            self.last_execution_time = execution_time
            
            # 设置执行时间
            result.execution_time = execution_time
            
            # 更新状态
            self.status = ToolStatus.SUCCESS if result.success else ToolStatus.ERROR
            
            # 记录日志
            log_tool_usage(
                agent_id="",  # 这里应该从上下文获取
                tool_name=self.schema.name,
                success=result.success,
                duration=execution_time
            )
            
            logger.info(
                f"工具执行完成: {self.schema.name}, "
                f"成功: {result.success}, "
                f"耗时: {execution_time:.2f}秒"
            )
            
            return result
            
        except ToolError:
            self.status = ToolStatus.ERROR
            raise
        except Exception as e:
            self.status = ToolStatus.ERROR
            execution_time = time.time() - start_time
            
            logger.error(f"工具执行异常: {self.schema.name}, 错误: {str(e)}")
            
            return ToolResult(
                success=False,
                error=f"工具执行失败: {str(e)}",
                execution_time=execution_time
            )
        finally:
            # 如果不是正在运行状态，恢复为空闲
            if self.status == ToolStatus.RUNNING:
                self.status = ToolStatus.IDLE
    
    def _validate_parameters(self, params: Dict[str, Any]):
        """
        验证输入参数
        
        Args:
            params: 输入参数
            
        Raises:
            ToolError: 参数验证失败
        """
        for param_def in self.schema.parameters:
            param_name = param_def.name
            param_value = params.get(param_name)
            
            # 检查必需参数
            if param_def.required and param_value is None:
                raise ToolError(
                    self.schema.name,
                    f"缺少必需参数: {param_name}"
                )
            
            # 如果参数存在，进行类型和值验证
            if param_value is not None:
                self._validate_parameter_value(param_def, param_value)
    
    def _validate_parameter_value(self, param_def: ToolParameter, value: Any):
        """
        验证单个参数值
        
        Args:
            param_def: 参数定义
            value: 参数值
            
        Raises:
            ToolError: 参数值验证失败
        """
        # 类型验证
        expected_types = {
            "string": str,
            "integer": int,
            "number": (int, float),
            "boolean": bool,
            "array": list,
            "object": dict
        }
        
        expected_type = expected_types.get(param_def.type)
        if expected_type and not isinstance(value, expected_type):
            raise ToolError(
                self.schema.name,
                f"参数 {param_def.name} 类型错误，期望: {param_def.type}，实际: {type(value).__name__}"
            )
        
        # 枚举值验证
        if param_def.enum and value not in param_def.enum:
            raise ToolError(
                self.schema.name,
                f"参数 {param_def.name} 值无效，必须是: {param_def.enum} 中的一个"
            )
        
        # 数值范围验证
        if isinstance(value, (int, float)):
            if param_def.min_value is not None and value < param_def.min_value:
                raise ToolError(
                    self.schema.name,
                    f"参数 {param_def.name} 值过小，最小值: {param_def.min_value}"
                )
            if param_def.max_value is not None and value > param_def.max_value:
                raise ToolError(
                    self.schema.name,
                    f"参数 {param_def.name} 值过大，最大值: {param_def.max_value}"
                )
        
        # 字符串模式验证
        if isinstance(value, str) and param_def.pattern:
            import re
            if not re.match(param_def.pattern, value):
                raise ToolError(
                    self.schema.name,
                    f"参数 {param_def.name} 格式无效，必须匹配模式: {param_def.pattern}"
                )
    
    def get_status(self) -> Dict[str, Any]:
        """
        获取工具状态信息
        
        Returns:
            状态信息
        """
        avg_execution_time = (
            self.total_execution_time / self.execution_count
            if self.execution_count > 0 else 0.0
        )
        
        return {
            "tool_id": self.tool_id,
            "name": self.schema.name,
            "type": self.schema.tool_type.value,
            "status": self.status.value,
            "enabled": self.schema.enabled,
            "execution_count": self.execution_count,
            "total_execution_time": self.total_execution_time,
            "average_execution_time": avg_execution_time,
            "last_execution_time": self.last_execution_time,
            "created_at": self.created_at
        }
    
    def enable(self):
        """启用工具"""
        self.schema.enabled = True
        logger.info(f"工具已启用: {self.schema.name}")
    
    def disable(self):
        """禁用工具"""
        self.schema.enabled = False
        logger.info(f"工具已禁用: {self.schema.name}")
    
    def reset_stats(self):
        """重置统计信息"""
        self.execution_count = 0
        self.total_execution_time = 0.0
        self.last_execution_time = 0.0
        logger.info(f"工具统计信息已重置: {self.schema.name}")


# 工具装饰器
def tool(
    name: str,
    description: str,
    tool_type: ToolType = ToolType.CUSTOM,
    timeout: int = 30,
    parameters: Optional[List[ToolParameter]] = None
):
    """
    工具装饰器，用于快速创建工具
    
    Args:
        name: 工具名称
        description: 工具描述
        tool_type: 工具类型
        timeout: 超时时间
        parameters: 参数列表
    """
    def decorator(func):
        class DecoratedTool(BaseTool):
            def get_schema(self) -> ToolSchema:
                return ToolSchema(
                    name=name,
                    description=description,
                    tool_type=tool_type,
                    timeout=timeout,
                    parameters=parameters or []
                )
            
            async def _execute(self, **kwargs) -> ToolResult:
                try:
                    if asyncio.iscoroutinefunction(func):
                        result = await func(**kwargs)
                    else:
                        result = func(**kwargs)
                    
                    return ToolResult(success=True, data=result)
                except Exception as e:
                    return ToolResult(success=False, error=str(e))
        
        return DecoratedTool
    
    return decorator


# 导出
__all__ = [
    "BaseTool",
    "ToolResult",
    "ToolSchema",
    "ToolParameter",
    "ToolType",
    "ToolStatus",
    "tool",
]